package org.jupiter.blog.redis.component;

import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import java.util.HashMap;
import java.util.stream.Collectors;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.util.CollectionUtils;

/**
 * 基于 Spring RedisTemplate 的 Redis 工具类
 *
 * @author lwl
 * @date 2025/8/31
 */
@Component
public class SpringRedisRepository {

	@Resource(name = "jsonRedisTemplate")
	private RedisTemplate<String, Object> redisTemplate;

	private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
			.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

	/**
	 * 存入缓存
	 */
	public void set(String key, Object value) {
		redisTemplate.opsForValue().set(key, value);
	}

	/**
	 * 存入缓存并设置过期时间（分钟）
	 */
	public void set(String key, Object value, long expireTimeMinutes) {
		redisTemplate.opsForValue().set(key, value, expireTimeMinutes, TimeUnit.MINUTES);
	}

	/**
	 * 获取缓存
	 */
	public <T> T get(String key, Class<T> clazz) {
		Object value = redisTemplate.opsForValue().get(key);
		if (value == null)
			return null;
		// 优先使用类型安全的转换
		if (clazz.isInstance(value)){
			return clazz.cast(value);
		}
		// 当使用通用 JSON 反序列化且没有类型信息时，可能得到 LinkedHashMap 等中间类型
		return OBJECT_MAPPER.convertValue(value, clazz);
	}

	/**
	 * 获取过期时间（秒）
	 */
	public long ttl(String key) {
		return redisTemplate.getExpire(key, TimeUnit.SECONDS);
	}

	/**
	 * 删除缓存
	 */
	public boolean remove(String key) {
		return redisTemplate.delete(key);
	}

	/**
	 * 递增
	 */
	public long increment(String key, long delta, int expireMinutes) {
		Long num = redisTemplate.opsForValue().increment(key, delta);
		redisTemplate.expire(key, expireMinutes, TimeUnit.MINUTES);
		return num == null ? 0L : num;
	}

	/**
	 * 递减
	 */
	public long decrement(String key, long delta, int expireMinutes) {
		Long num = redisTemplate.opsForValue().increment(key, -delta);
		redisTemplate.expire(key, expireMinutes, TimeUnit.MINUTES);
		return num == null ? 0L : num;
	}

	/**
	 * 设置过期时间（秒）
	 */
	public void expire(String key, long seconds) {
		redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
	}

	/**
	 * 获取数值（long）
	 */
	public long getLong(String key) {
		Object value = redisTemplate.opsForValue().get(key);
		if (value instanceof Number){
			return ((Number) value).longValue();
		}
		return 0L;
	}

	/**
	 * 判断 key 是否存在
	 */
	public boolean hasKey(String key) {
		return redisTemplate.hasKey(key);
	}

	/**
	 * 批量获取 key（支持通配符）
	 */
	public Set<String> getKeysByPattern(String pattern) {
		return redisTemplate.keys(pattern);
	}

	// =============== 扩展：Set/Hash 基础能力 ===============

	public Long setAdd(String key, String... members) {
		return redisTemplate.opsForSet().add(key, (Object[]) members);
	}

	public Set<String> setMembers(String key) {
		Set<Object> raw = redisTemplate.opsForSet().members(key);
		if (raw == null)
			return Set.of();
		return raw.stream().map(String::valueOf).collect(Collectors.toSet());
	}

	public Long hashSet(String key, Map<String, String> map) {
		redisTemplate.opsForHash().putAll(key, map);
		return (long) map.size();
	}

	public Map<String, String> hashGetAllString(String key) {
		Map<Object, Object> raw = redisTemplate.opsForHash().entries(key);
		if (CollectionUtils.isEmpty(raw))
			return Map.of();
		Map<String, String> result = new HashMap<>();
		raw.forEach((k, v) -> result.put(String.valueOf(k), v == null ? null : String.valueOf(v)));
		return result;
	}

	/**
	 * 计算两个Set的交集
	 */
	public Set<String> setIntersect(String key1, String key2) {
		Set<Object> raw = redisTemplate.opsForSet().intersect(key1, key2);
		if (raw == null)
			return Set.of();
		return raw.stream().map(String::valueOf).collect(Collectors.toSet());
	}

	/**
	 * 计算多个Set的交集
	 */
	public Set<String> setIntersect(String... keys) {
		Set<Object> raw = redisTemplate.opsForSet().intersect(java.util.Arrays.asList(keys));
		if (raw == null)
			return Set.of();
		return raw.stream().map(String::valueOf).collect(Collectors.toSet());
	}

	/**
	 * 计算两个Set的交集并返回交集大小
	 */
	public Long setIntersectSize(String key1, String key2) {
		return redisTemplate.opsForSet().intersectAndStore(key1, key2, "temp:intersect:" + System.currentTimeMillis());
	}

	/**
	 * 检查Set中是否包含指定成员
	 */
	public Boolean setIsMember(String key, String member) {
		return redisTemplate.opsForSet().isMember(key, member);
	}

}
